﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using MoneyWentWhere.Utils;
using System.Collections;

namespace MoneyWentWhere.OutputHelpers
{
    public abstract class Column
    {
        public string Header;
        public List<string> Values = new List<string>();
        public int Width;

        // Options from the parent
        public TableOptions TableOptions;

        public Column(TableOptions options)
        {
            TableOptions = options;
        }

        public abstract void AddValues<T>(IEnumerable<T> rows);

        public abstract String GetPaddedHeader();
        public abstract String GetPaddedValue(int rowIndex);

        #region Utility methods

        protected int GetMaxValueWidth()
        {
            int width = 0;
            // Determine width based on values
            width = Values.Select(x => x.Length).MaxOrDefault(0);

            // Consider the title
            if (TableOptions.ShowFullColumnName)
            {
                width = Math.Max(width, Header.Length);
            }

            return width;
        }
        #endregion
    }

    public abstract class ValueColumn : Column
    {
        readonly DisplayConverter Converter;

        public ValueColumn(TableOptions options, MemberInfo member)
            : base(options)
        {
            Converter = GetConverter(member);
        }

        public abstract object GetValue<T>(T row);

        public sealed override void AddValues<T>(IEnumerable<T> rows)
        {
            Values = new List<string>();
            foreach (T row in rows)
            {
                object value = GetValue(row);
                String strVal = "";
                if (value != null)
                {
                    strVal = Converter.FieldToString(value);
                }
                Values.Add(strVal);
            }

            Width = GetMaxValueWidth();
        }

        public override String GetPaddedHeader()
        {
            return Header.TrimOrPad(Width, Converter.Alignment);
        }

        public override string GetPaddedValue(int rowIndex)
        {
            return Values[rowIndex].TrimOrPad(Width, Converter.Alignment);
        }
            
        static DisplayConverter GetConverter(MemberInfo member)
        {
            DisplayConverterAttribute att = member.GetAttribute<DisplayConverterAttribute>();
            if (att == null)
            {
                return new DefaultDisplayConverter();
            }
            else
            {
                return (DisplayConverter)Activator.CreateInstance(att.ConverterType);
            }
        }

    }

    public abstract class MultiColumn : Column
    {
        protected List<Column> Columns = null;

        public MultiColumn(TableOptions options)
            : base(options)
        {
        }

        public sealed override void AddValues<T>(IEnumerable<T> rows)
        {
            // Add values, determine widths
            InitializeSubcolumns(rows);

            foreach (Column col in Columns)
            {
                col.AddValues(rows);
            }

            Header = GetPaddedHeader();

            Values = new List<string>();
            int rowCount = rows.Count();
            for (int rowIndex = 0; rowIndex < rowCount; rowIndex++)
            {
                String strVal = GetPaddedValue(rowIndex);
                Values.Add(strVal);
            }

            Width = GetMaxValueWidth();
        }

        protected abstract void InitializeSubcolumns<T>(IEnumerable<T> rows);

        public override string GetPaddedHeader()
        {
            if (Columns == null) { throw new InvalidOperationException("Columns not initialized"); }

            StringBuilder sb = new StringBuilder();

            bool isFirst = true;
            foreach (Column col in Columns)
            {
                if (!isFirst) { sb.Append(TableOptions.ColumnSeparator); }
                isFirst = false;

                String strVal = col.GetPaddedHeader();
                sb.Append(strVal);
            }

            return sb.ToString();
        }

        public override string GetPaddedValue(int rowIndex)
        {
            if (Columns == null) { throw new InvalidOperationException("Columns not initialized"); }
            if (Values == null) { throw new InvalidOperationException("Values not initialized"); }

            StringBuilder sb = new StringBuilder();
            bool isFirst = true;
            foreach (Column col in Columns)
            {
                if (!isFirst) { sb.Append(TableOptions.ColumnSeparator); }
                isFirst = false;

                String strVal = col.GetPaddedValue(rowIndex);
                sb.Append(strVal);
            }
            return sb.ToString();
        }
    }

}
